package executor.register.processor;

import com.xxl.job.core.handler.annotation.XxlJob;
import executor.register.annotation.JobHandlerAutoRegister;
import executor.register.client.JobInfoService;
import executor.register.model.XxlJobInfo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationListener;
import org.springframework.core.MethodIntrospector;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;

@Slf4j
public class XxjJobAutoRegisterBeanPostProcessor implements ApplicationListener<ApplicationReadyEvent>,
        ApplicationContextAware, BeanPostProcessor {

    private ApplicationContext applicationContext;

    @Autowired
    private JobInfoService jobInfoService;

    private List<XxlJobInfo> xxlJobInfos = new ArrayList<>();

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }


    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        //获取方法和对应的注解配置
        Map<Method, XxlJob> annotatedMethods = MethodIntrospector.selectMethods(bean.getClass(),
                (MethodIntrospector.MetadataLookup<XxlJob>) method ->
                        AnnotatedElementUtils.findMergedAnnotation(method, XxlJob.class));

        for (Map.Entry<Method, XxlJob> methodXxlJobEntry : annotatedMethods.entrySet()) {
            Method executeMethod = methodXxlJobEntry.getKey();
            XxlJob xxlJob = methodXxlJobEntry.getValue();

            //自动注册
            if (executeMethod.isAnnotationPresent(JobHandlerAutoRegister.class)) {
                JobHandlerAutoRegister xxlRegister = executeMethod.getAnnotation(JobHandlerAutoRegister.class);
                //查询组下面jobInfo是不是已注册，
                List<XxlJobInfo> jobInfo = jobInfoService.getJobInfo(xxlRegister.jobGroup(), xxlJob.value());
                //在admin中其实是按照handler进行模糊查询的，因此还要精确的判断一次
                if (!jobInfo.isEmpty()) {
                    //因为是模糊查询，需要再判断一次
                    Optional<XxlJobInfo> first = jobInfo.stream()
                            .filter(xxlJobInfo -> xxlJobInfo.getExecutorHandler().equals(xxlJob.value()))
                            .findFirst();
                    if (first.isPresent())
                        continue;
                }

                XxlJobInfo xxlJobInfo = createXxlJobInfo(xxlJob, xxlRegister);
                xxlJobInfos.add(xxlJobInfo);
            }
        }
        return bean;
    }

    private XxlJobInfo createXxlJobInfo(XxlJob xxlJob, JobHandlerAutoRegister xxlRegister) {
        XxlJobInfo xxlJobInfo = new XxlJobInfo();
        xxlJobInfo.setJobGroup(xxlRegister.jobGroup());
        xxlJobInfo.setJobDesc(xxlRegister.jobDesc());
        xxlJobInfo.setAuthor(xxlRegister.author());
        xxlJobInfo.setScheduleType("CRON");
        xxlJobInfo.setScheduleConf(xxlRegister.jobCron());
        xxlJobInfo.setGlueType("BEAN");
        xxlJobInfo.setExecutorHandler(xxlJob.value());
        xxlJobInfo.setExecutorRouteStrategy(xxlRegister.executorRouteStrategy());
        xxlJobInfo.setMisfireStrategy("DO_NOTHING");
        xxlJobInfo.setExecutorBlockStrategy("SERIAL_EXECUTION");
        xxlJobInfo.setExecutorTimeout(0);
        xxlJobInfo.setExecutorFailRetryCount(0);
        xxlJobInfo.setGlueRemark("GLUE代码初始化");
        xxlJobInfo.setTriggerStatus(0);

        return xxlJobInfo;
    }

    @Override
    public void onApplicationEvent(ApplicationReadyEvent event) {
        //注册所有的任务
        xxlJobInfos.forEach(xxlJobInfo -> {
            try {
                jobInfoService.addJobInfo(xxlJobInfo);
            } catch (Exception e) {
                log.error("自动添加xxl-job失败！！请手动排查 xxlJobInfo = [{}]", xxlJobInfo, e);
            }
        });
    }
}
